package top.juc.plugin.idea.openfeignx.util;

import com.intellij.psi.PsiAnnotationMemberValue;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiReferenceExpression;
import org.jetbrains.annotations.NotNull;
import top.juc.plugin.idea.openfeignx.annotation.Annotation;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

public class SpringMvcUtils {

    private static final String ANNOTATION_FEIGN_NAME = "name";

    private static final String ANNOTATION_FEIGN_VALUE = "value";

    private static final String ANNOTATION_FEIGN_PATH = "path";

    public static ConcurrentHashMap<String, PsiMethod> getRestControllerPath(List<PsiClass> clazzList) {
        if(CollectionUtils.isEmpty(clazzList)){
            return new ConcurrentHashMap<>();
        }
        ConcurrentHashMap<String, PsiMethod> resultMap = new ConcurrentHashMap<>(512);
        for (PsiClass psiClass : clazzList) {
            String projectName = CacheUtils.getModuleSpringApplicationName(psiClass);
            String prefixPath = JavaUtils.getAnnotationValueText(psiClass, Annotation.REQUEST_MAPPING).orElse("");
            for (PsiMethod psiMethod : psiClass.getMethods()) {
                String key = getFullPath(projectName, prefixPath, getMethodPath(psiMethod));
                resultMap.put(key, psiMethod);
            }
        }
        return resultMap;
    }

    public static ConcurrentHashMap<String, PsiMethod> getFeignPath(List<PsiClass> clazzList) {
        if(CollectionUtils.isEmpty(clazzList)){
            return new ConcurrentHashMap<>();
        }
        ConcurrentHashMap<String, PsiMethod> resultMap = new ConcurrentHashMap<>(512);
        for (PsiClass psiClass : clazzList) {
            String projectName = getFeignProjectName(psiClass,ANNOTATION_FEIGN_NAME);
            if(org.apache.commons.lang3.StringUtils.isBlank(projectName)){
                projectName = getFeignProjectName(psiClass,ANNOTATION_FEIGN_VALUE);
            }
            String prefixPath = JavaUtils.getAnnotationAttributeValue(psiClass, Annotation.FEIGN_CLIENT, ANNOTATION_FEIGN_PATH).map(psiAnnotationMemberValue -> psiAnnotationMemberValue.getText().replaceAll("\"", "")).orElse("");
            for (PsiMethod psiMethod : psiClass.getMethods()) {
                String key = getFullPath(projectName, prefixPath, getMethodPath(psiMethod));
                resultMap.put(key, psiMethod);
            }
        }
        return resultMap;
    }


    private static String getFeignProjectName(PsiClass psiClass,String annotationParamName){
        Optional<PsiAnnotationMemberValue> feignNameOptional = JavaUtils.getAnnotationAttributeValue(psiClass, Annotation.FEIGN_CLIENT, annotationParamName);
        String projectName = feignNameOptional.map(psiAnnotationMemberValue -> psiAnnotationMemberValue.getText().replaceAll("\"", "")).orElse("");
        if (feignNameOptional.isPresent()) {
            PsiAnnotationMemberValue value = feignNameOptional.get();
            if (value instanceof PsiExpression) {
                return evaluateConstantExpression((PsiExpression) value);
            }
        }
        return projectName;
    }



    private static String evaluateConstantExpression(PsiExpression expression) {
        if (expression instanceof PsiLiteralExpression) {
            PsiLiteralExpression refExpression = (PsiLiteralExpression) expression;
            return Optional.ofNullable(refExpression.getValue()).map(Object::toString).orElse("");
        } else if (expression instanceof PsiReferenceExpression) {
            PsiReferenceExpression refExpression = (PsiReferenceExpression) expression;
            PsiElement resolve = refExpression.resolve();
            if(resolve instanceof PsiField){
                PsiField field = (PsiField) resolve;
                // 获取其值之前需要确保它是一个静态字段
                if (field.hasModifierProperty(PsiModifier.STATIC)) {
                    // 获取值
                    return Optional.ofNullable(field.computeConstantValue()).map(Object::toString).orElse("");
                }
            }
        }
        return "";
    }

    public static String getMethodPath(PsiMethod psiMethod){
        if (JavaUtils.isAnnotationPresent(psiMethod, Annotation.GET_MAPPING)) {
            return getMethodPath(psiMethod, Annotation.GET_MAPPING);
        } else if (JavaUtils.isAnnotationPresent(psiMethod, Annotation.POST_MAPPING)) {
            return getMethodPath(psiMethod, Annotation.POST_MAPPING);
        } else if (JavaUtils.isAnnotationPresent(psiMethod, Annotation.PUT_MAPPING)) {
            return getMethodPath(psiMethod, Annotation.PUT_MAPPING);
        } else if (JavaUtils.isAnnotationPresent(psiMethod, Annotation.DELETE_MAPPING)) {
            return getMethodPath(psiMethod, Annotation.DELETE_MAPPING);
        } else if (JavaUtils.isAnnotationPresent(psiMethod, Annotation.REQUEST_MAPPING)) {
            return getMethodPath(psiMethod, Annotation.REQUEST_MAPPING);
        }
        return "";
    }

    private static String getMethodPath(PsiMethod psiMethod, @NotNull Annotation annotation) {
        Optional<String> optional = JavaUtils.getAnnotationValueText(psiMethod, annotation);
        return optional.map(s -> s + "/" + annotation.getLabel()).orElseGet(annotation::getLabel);
    }


    public static String getFullPath(String projectName, String prefixPath, String methodPath) {
        projectName = StringUtils.removeStartAndEnd(projectName, "/");
        prefixPath = StringUtils.removeStartAndEnd(prefixPath, "/");
        methodPath = StringUtils.removeStartAndEnd(methodPath, "/");
        return StringUtils.joinStr(projectName, prefixPath, methodPath);
    }


}
